26. Exercise: Add a ChipGroup
L10 45 Stateful Drawables SC
Now it's time for you to complete this exercise yourself!
In this exercise, you'll add a row of buttons, called Chips, to the top of the Search screen. Each one will filter the data so you'll see results from the selected region.
This exercise includes quite a few steps, so we'll break it down into sections for you.
Create the layouts:
1. Create a ChipGroup.
In fragment_gdg_list.xml, create a ChipGroup inside the ScrollView, and set its singleLine and singleSelection to true.
<com.google.android.material.chip.ChipGroup
android:id="@+id/region_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="wrap_content"
app:singleSelection="true"
android:padding="@dimen/spacing_normal"/>
2. Create a Chip layout.
Open region.xml, which we’ve provided for you in the project. Replace the LinearLayout with a Chip layout. Set its width and height to wrap_content.
<com.google.android.material.chip.Chip
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
3. Style the Chip.
Add Chip.Choice style to the Chip.**
style="@style/Widget.MaterialComponents.Chip.Choice"
As we showed you in the video, you can also use Android Studio's autocomplete feature to locate style. A Chip is a Widget component, so try typing in style=”W and letting autocomplete help you search for the correct Chip style.
Create an observer for defining a ChipGroup and adding Chips:
1. Add an observer on viewModel.regionList.
In GdgListFragment, add an observer on viewModel.regionList and override onChanged(), adding a statement to return immediately if data is null.
viewModel.regionList.observe(viewLifecycleOwner, object: Observer<List<String>> {
override fun onChanged(data: List<String>?) {
data ?: return
}
})
2. Create chipGroup and inflator variables.
Inside onChanged(), assign binding.regionList to a new variable called chipGroup, and create a new layoutInflator from chipGroup.context.
val chipGroup = binding.regionList
val inflator = LayoutInflater.from(chipGroup.context)
3. Create a Chip for each regionsList item.
Use the map function to iterate over regionsList and create a Chip for each item, then return the results as a new list called children.
Set each Chip’s text and tag to regionName. Also set an OnCheckedChangeListener so that we can use the tag to get the regionName for the selected Chip.
val children = data.map { regionName ->
val chip = inflator.inflate(R.layout.region, chipGroup, false) as Chip
chip.text = regionName
chip.tag = regionName
chip.setOnCheckedChangeListener { button, isChecked ->
viewModel.onFilterChanged(button.tag as String, isChecked)
}
chip
}
4. Remove views that are already in chipGroup.
Add a call to call chipGroup.removeAllViews() after the data.map code, .
5. Finally, iterate through the list of children to add each chip to chipGroup:
for (chip in children) {
chipGroup.addView(chip)
}
Your completed observer should look like this:
viewModel.regionList.observe(viewLifecycleOwner, object: Observer<List<String>> {
override fun onChanged(data: List<String>?) {
data ?: return
val chipGroup = binding.regionList
val inflator = LayoutInflater.from(chipGroup.context)
val children = data.map { regionName ->
val chip = inflator.inflate(R.layout.region, chipGroup, false) as Chip
chip.text = regionName
chip.tag = regionName
chip.setOnCheckedChangeListener { button, isChecked ->
viewModel.onFilterChanged(button.tag as String, isChecked)
}
chip
}
chipGroup.removeAllViews()
for (chip in children) {
chipGroup.addView(chip)
}
}
})
Run your app and open the Search screen to see your new chips. As you click each chip you’ll see its filter groups below.
Now let’s add some code to style the chips when they’re selected.
1. Show check mark next to selected chip.
In region.xml, add checkedIconVisible=true to the Chip.
You can also add tools:checked=”true” so you can preview the chip in its checked state.**
app:checkedIconVisible="true"
tools:checked="true"
2. To change the chip’s background color only when it’s selected.
Add a app:chipBackgroundColor="@color/selected_highlight", then use Alt-Enter to create the color resource file.
The file will be called selected_highlight.xml and will be in the res/color folder.
3. Assign colors for view states.
Open selected_highlight.xml, and add colors to the selector block. The colors we’re using are from the Material theme:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorPrimaryVariant" android:state_selected="true" />
<item android:color="?colorOnSurface" android:alpha="0.18" />
</selector>
Run your app again and verify the background color changes every time a chip is selected.
If you want to start at this step, you can download this exercise from: Step.12-Exercise-Add-a-ChipGroup.
You will find plenty of //TODO comments to help you complete this exercise, and if you get stuck, go back and watch the video again.
Once you’re done, you can check your solution against the solution we’ve provided here: Step.12-Solution-Add-a-ChipGroup, or using this git diff.
Task Description:
Use cardview + stateful drawable to select filters
Task Feedback:
That was a lot of work, but you did it!